#pragma once

#include <vector>
#include <unordered_map>
#include <list>
#include <mutex>
#include "Common.hpp"
#include "./include/cppjieba/include/cppjieba/Jieba.hpp"


// 定义词典路径
const std::string DICT_PATH = "./include/cppjieba/dict/jieba.dict.utf8";
const std::string HMM_PATH = "./include/cppjieba/dict/hmm_model.utf8";
const std::string USER_DICT_PATH = "./include/cppjieba/dict/user.dict.utf8";
const std::string STOP_WORD_PATH = "./include/cppjieba/dict/stop_words.utf8";
const std::string IDF_WORD_PATH = "./include/cppjieba/dict/idf.utf8";


class JiebaWrapper
{
private:
    JiebaWrapper()
    :_jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_WORD_PATH, STOP_WORD_PATH)
    {
        std::ifstream ifs(STOP_WORD_PATH);
        std::string word;
        while(ifs >> word)
        {
            _stop_words.insert(word);
        }
    }
public:
    JiebaWrapper(const JiebaWrapper&) = delete;
    JiebaWrapper& operator=(const JiebaWrapper&) = delete;

    void segment_noStop(const std::string &text  , std::vector<std::string>& words)
    {
        segment(text , words);
        std::list<std::string> list_words(words.begin() , words.end());
        auto it = list_words.begin();
        while(it != list_words.end())
        {
            if(_stop_words.count(*it))
            {
                // 是暂停词,直接删除
                it = list_words.erase(it);
            }
            else 
                it++;
        }

        // 将数据从list中拷贝回来
        std::vector<std::string> tmp(list_words.begin() , list_words.end());
        words.swap(tmp);
    }

    void segment(const std::string &text  , std::vector<std::string>& words)
    {
        _jieba.CutForSearch(text, words);
    }

    static std::shared_ptr<JiebaWrapper> Get_Jieba()
    {
        if(_jiebawrapper == nullptr)
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if(_jiebawrapper == nullptr)
            {
                _jiebawrapper = std::shared_ptr<JiebaWrapper>(new JiebaWrapper);
            }
        }
        return _jiebawrapper;
    }

private:
    std::unordered_set<std::string> _stop_words;
    cppjieba::Jieba _jieba;
    static std::shared_ptr<JiebaWrapper> _jiebawrapper;
    static std::mutex _mtx;
};
std::shared_ptr<JiebaWrapper> JiebaWrapper::_jiebawrapper = nullptr;
std::mutex JiebaWrapper::_mtx;




class Index
{
    void Add_Into_Forward(std::shared_ptr<html_base> base_ptr)
    {
        base_ptr->_fileno = _forward_info.size();
        _forward_info.push_back(base_ptr);
    }
    void Add_Into_Invert(std::shared_ptr<html_base> base_ptr)
    {
        // 1. 对标题和正文进行分词
        std::vector<std::string> title_words;
        std::vector<std::string> content_words;
        std::shared_ptr<JiebaWrapper> jbWrapper = JiebaWrapper::Get_Jieba();

        jbWrapper->segment_noStop(base_ptr->_title , title_words);
        jbWrapper->segment_noStop(base_ptr->_content , content_words);

        // 2. 对分词结果进行统计
        std::unordered_map<std::string , word_cnt> str_cnt;
        for(auto& word : title_words)
            str_cnt[word]._title_cnt++;
        for(auto& word : content_words)
            str_cnt[word]._content_cnt++;
        
        // 3. 将分词结果加入到倒排数组中
        size_t file_no = base_ptr->_fileno;
        for(auto& [word , _cnt] : str_cnt)
        {
            std::shared_ptr<inverted_base> inv_ptr(new inverted_base);
            inv_ptr->_fileno = file_no;
            inv_ptr->_word = word;
            inv_ptr->_weight = _cnt._title_cnt*500 + (_cnt._content_cnt - _cnt._title_cnt)*1;

            _inverted_info[word].push_back(inv_ptr);
        }
    }

public:
    // 向索引中添加HTML文件
    void Insert(std::shared_ptr<html_base> html_ptr)
    {
        // 插入数据
        // 1. 插入到正排数组中
        // 2. 插入到倒排数组中
        Add_Into_Forward(html_ptr);
        Add_Into_Invert(html_ptr);
    }

    // 根据文件编号获取文件内容
    std::shared_ptr<html_base> Get_By_Fileno(int fileno)
    {
        return _forward_info[fileno];
    }

    // 根据关键字获取对应的文件
    std::vector<std::shared_ptr<inverted_base>> &Get_By_Word(const std::string &word)
    {
        return _inverted_info[word];
    }

private:
    std::vector<std::shared_ptr<html_base>> _forward_info;
    std::unordered_map<std::string, std::vector<std::shared_ptr<inverted_base>>> _inverted_info;
};